
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ServerThread implements Runnable {

    private Socket socket = null;
    private InputStream in;
    private OutputStream out;
    private DataInputStream dataIn;
    private DataOutputStream dataOut;
    private String dictionaryWord;
    private List<String> foundCharacters = new ArrayList<String>();
    private int attemptLeft = 0;
    private final int numberOfDictionaryEntries = 25142;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        try {
            in = socket.getInputStream();
            out = socket.getOutputStream();
            dataIn = new DataInputStream(in);
            dataOut = new DataOutputStream(out);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            startGame();
            String strEmpty = createResponseWithUnderscore(dictionaryWord, foundCharacters);
            dataOut.writeChars(strEmpty);
            dataOut.writeChars("0");
            dataOut.flush();

            char receivedChar;
            while ((receivedChar = dataIn.readChar()) != -1) {
                System.out.println("Receive letter: " + receivedChar);
                // response message
                checkAnswer(receivedChar);
                if (isGameOver()) {
                    break;
                }

            }
            dataIn.close();
            dataOut.close();
            socket.close();
        } catch (IOException e) {
            //e.printStackTrace();
            System.out.println("connection closed");
        }
    }

    /**
     * function to call when the game is started
     */
    private void startGame() {
        System.out.println("start game");
        try {
            selectRandomWordInDictionary();
            foundCharacters.clear();
            attemptLeft = dictionaryWord.length() + 1;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * function to call to verify if the game is over or not
     */
    private boolean isGameOver() {
        if (attemptLeft == 0) {
            try {
                dataOut.writeChars(dictionaryWord);
                dataOut.flush();
                dataOut.writeChars("2");
                dataOut.flush();
            } catch (IOException ex) {
                Logger.getLogger(ServerThread.class.getName()).log(Level.SEVERE, null, ex);
            }
            return true;
        }

        if (testIfGameWin()) {
            try {
                dataOut.writeChars(dictionaryWord);
                dataOut.flush();
                dataOut.writeChars("1");
                dataOut.flush();
            } catch (IOException ex) {
                Logger.getLogger(ServerThread.class.getName()).log(Level.SEVERE, null, ex);
            }
            return true;
        }
        return false;
    }

    /**
     * Function to check the answer
     * 
     * @param arg: letter or word send by the client
     */
    private void checkAnswer(char arg) {
        String str = arg + "";
        addNewLetterToFoundList(str);
    }

    /**
     * If a new character of the dictionary word has been found, this function will add this new character
     * to the list of found characters 
     * @param arg
     * @param responseMsg
     */
    private void addNewLetterToFoundList(String arg) {
        if (dictionaryWord.contains(arg)
                && !foundCharacters.contains(arg)) {
            foundCharacters.add(arg);
        } else {
            attemptLeft--;
        }
        String answerToSend = createResponseWithUnderscore(dictionaryWord, foundCharacters);
        try {
            dataOut.writeChars(answerToSend);
            dataOut.writeChars("0");
            dataOut.flush();
        } catch (IOException ex) {
            Logger.getLogger(ServerThread.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * This function creates a string for the hangman UI with underscores for
     * unknown letters
     * 
     * @param str
     *            the word from the dictionary
     * @param list
     *            the list of found characters
     * @return
     */
    private String createResponseWithUnderscore(String dicoWord,
            List<String> list) {
        StringBuilder response = new StringBuilder();
        int i = 0;
        while (i < dicoWord.length()) {
            response.append("_ ");
            i++;
        }
        for (String string : list) {
            int index = dicoWord.indexOf(string);

            while (index != -1) {
                response.replace(2 * index, 2 * index + 1, string);
                index = dicoWord.indexOf(string, index + 1);
            }
        }

        return response.toString();
    }

    /**
     * return true if all the letters of the word has been found
     * or false otherwise 
     * @return
     */
    private Boolean testIfGameWin() {
        for (int i = 0; i < dictionaryWord.length(); i++) {
            if (!foundCharacters.contains(String.valueOf(dictionaryWord.charAt(i)))) {
                return false;
            }
        }
        return true;
    }

    /**
     * returns a random word found in the given dictionary
     * @throws IOException
     */
    private synchronized void selectRandomWordInDictionary() throws IOException {
        BufferedReader inputStream = null;
        Random r = new Random();
        int entryNumber = r.nextInt(numberOfDictionaryEntries);
        try {
            try {
                inputStream = new BufferedReader(new FileReader("words.txt"));
            } catch (FileNotFoundException e) {
                System.out.println("file words.txt not found");
                e.printStackTrace();
            }

            String l = null;
            int i = 0;
            while ((l = inputStream.readLine()) != null && (i != entryNumber)) {
                i++;
            }
            dictionaryWord = l.toLowerCase();
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }

        System.out.println("message chose in dictionary " + dictionaryWord);
    }
}
